﻿namespace Jsl.FxCop
{
    using System;
    using System.Drawing;
    using System.Linq;
    using System.Resources;
    using System.Windows.Forms;
    using Microsoft.FxCop.Sdk;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class TestComponentDictionary
    {
        #region ComponentAccessors Tests
        [TestMethod]
        public void No_fields_have_the_AccessedThroughPropertyAttribute()
        {
            var components = Test(
                @"
                    public class Class
                    {
                        private object myObject;
                    }
                ");

            AssertEx.That(() => components.ComponentAccessors.Count == 0);
        }

        [TestMethod]
        public void Fields_have_the_AccessedThroughPropertyAttribute_but_no_property()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;

                    public class Class
                    {
                        [AccessedThroughPropertyAttribute(""MyObject"")]
                        private object myObject;
                    }
                ");

            AssertEx.That(() => components.ComponentAccessors.Count == 0);
        }

        [TestMethod]
        public void Field_has_AccessedThroughPropertyAttribute_and_property()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;

                    public class Class
                    {
                        [AccessedThroughPropertyAttribute(""MyObject"")]
                        private object myObject;

                        public object MyObject
                        {
                            get
                            {
                                return this.myObject;
                            }
                        }
                    }
                ");

            AssertEx.That(() => components.ComponentAccessors.Count == 1);
            foreach (var accessor in components.ComponentAccessors)
            {
                AssertEx.That(() => accessor.Key.Name.Name == "MyObject");
                AssertEx.That(() => accessor.Value.Name.Name == "myObject");
            }
        }
        #endregion ComponentAccessors Tests

        [TestMethod]
        public void Controls_can_be_added_to_the_form()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.Controls.Add(this.textBox);
                        }
                    }
                ");

            AssertEx.That(() => components.Count == 1);

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));
            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            Node parent = null;
            AssertEx.That(() => textBoxInfo.TryGetValue("Parent", out parent));
            AssertEx.That(() => parent.NodeType == NodeType.This);
        }

        [TestMethod]
        public void Controls_can_be_added_to_the_container_controls()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private GroupBox groupBox;
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.groupBox = new GroupBox();
                            this.groupBox.Controls.Add(this.textBox);
                        }
                    }
                ");

            // Get the controls we care about.
            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));
            Field groupBoxField = components.Type.GetField(Identifier.For("groupBox"));

            // The textBox'es parent should be the groupBox.
            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            Node parent = null;
            AssertEx.That(() => textBoxInfo.TryGetValue("Parent", out parent));
            Assert.AreSame(groupBoxField, parent);
        }

        [TestMethod]
        public void TableLayoutPanel_parent_with_coordinates_is_found()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TableLayoutPanel table;
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.table = new TableLayoutPanel();
                            this.table.Controls.Add(this.textBox, 1, 2);
                        }
                    }
                ");

            // Get the controls we care about.
            Field tableField = components.Type.GetField(Identifier.For("table"));
            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            // The textBox'es parent should be the groupBox.
            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            Node parent = null;
            AssertEx.That(() => textBoxInfo.TryGetValue("Parent", out parent));
            Assert.AreSame(tableField, parent);
            TableLayoutPanelCellPosition cellPosition = default(TableLayoutPanelCellPosition);
            AssertEx.That(() => textBoxInfo.TryGetValue("CellPosition", out cellPosition));
            AssertEx.That(() => cellPosition.Column == 1);
            AssertEx.That(() => cellPosition.Row == 2);
        }

        [TestMethod]
        public void Localized_property_values_are_retrieved_from_the_embedded_resources()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private void InitializeComponent()
                        {
                            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Class));
                            resources.ApplyResources(this, ""$this"");
                        }
                    }
                ",
                writer =>
                {
                    writer.AddResource("$this.Size", new Size(100, 200));
                });

            // Find the information for the This node.
            var componentInfo =
                (from entry in components
                 where entry.Key is This
                 select entry.Value).Single();

            Size size = default(Size);
            AssertEx.That(() => componentInfo.TryGetValue("Size", out size));
            AssertEx.That(() => size == new Size(100, 200));
        }

        [TestMethod]
        public void ComponentInfo_contains_the_component_type()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.Controls.Add(this.textBox);
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            AssertEx.That(() => textBoxInfo.Type.FullName == "System.Windows.Forms.TextBox");
        }

        [TestMethod]
        public void Int32_property_settings_are_processed()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.textBox.TabIndex = 99;
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            int tabIndex = 0;
            AssertEx.That(() => textBoxInfo.TryGetValue("TabIndex", out tabIndex));
            AssertEx.That(() => tabIndex == 99);
        }

        [TestMethod]
        public void Boolean_property_settings_are_processed()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.textBox.Visible = true;
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            bool visible = false;
            AssertEx.That(() => textBoxInfo.TryGetValue("Visible", out visible));
            AssertEx.That(() => visible);
        }

        [TestMethod]
        public void String_property_settings_are_processed()
        {
            var components = Test(
                @"
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.textBox.Text = ""TESTING!"";
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            string text = null;
            AssertEx.That(() => textBoxInfo.TryGetValue("Text", out text));
            AssertEx.That(() => text == "TESTING!");
        }

        [TestMethod]
        public void Point_property_settings_are_processed()
        {
            var components = Test(
                @"
                    using System.Drawing;
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.textBox.Location = new Point(3, 4);
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            Point location = Point.Empty;
            AssertEx.That(() => textBoxInfo.TryGetValue("Location", out location));
            AssertEx.That(() => location == new Point(3, 4));
        }

        [TestMethod]
        public void Size_property_settings_are_processed()
        {
            var components = Test(
                @"
                    using System.Drawing;
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.textBox.Size = new Size(5, 6);
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            Size size = Size.Empty;
            AssertEx.That(() => textBoxInfo.TryGetValue("Size", out size));
            AssertEx.That(() => size == new Size(5, 6));
        }

        [TestMethod]
        public void SizeF_property_settings_are_processed()
        {
            var components = Test(
                @"
                    using System.Drawing;
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class MyControl : Control
                    {
                        public SizeF MySize
                        {
                            get
                            {
                                return SizeF.Empty;
                            }
                            set
                            {
                            }
                        }
                    }

                    public class Class : Form
                    {
                        private MyControl myControl;

                        private void InitializeComponent()
                        {
                            this.myControl = new MyControl();
                            this.myControl.MySize = new SizeF(7f, 8f);
                        }
                    }
                ");

            Field myControlField = components.Type.GetField(Identifier.For("myControl"));

            ComponentInfo myControlInfo = null;
            AssertEx.That(() => components.TryGetValue(myControlField, out myControlInfo));
            SizeF size = Size.Empty;
            AssertEx.That(() => myControlInfo.TryGetValue("MySize", out size));
            AssertEx.That(() => size == new SizeF(7f, 8f));
        }

        [TestMethod]
        public void Enum_property_settings_are_processed_value_is_integer()
        {
            var components = Test(
                @"
                    using System.Drawing;
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.textBox.Dock = DockStyle.Top;
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            int dockStyle = 0;
            AssertEx.That(() => textBoxInfo.TryGetValue("Dock", out dockStyle));
            AssertEx.That(() => dockStyle == 1);
        }

        [TestMethod]
        public void Field_property_settings_are_processed()
        {
            var components = Test(
                @"
                    using System.Drawing;
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private Button button;

                        private void InitializeComponent()
                        {
                            this.button = new Button();
                            this.AcceptButton = this.button;
                        }
                    }
                ");

            Field buttonField = components.Type.GetField(Identifier.For("button"));

            // Find the information for the This node.
            var componentInfo =
                (from entry in components
                 where entry.Key is This
                 select entry.Value).Single();

            Node acceptButtonControl = null;
            AssertEx.That(() => componentInfo.TryGetValue("AcceptButton", out acceptButtonControl));
            Assert.AreSame(buttonField, acceptButtonControl);
        }

        [TestMethod]
        public void Simple_Font_properties_are_processed()
        {
            var components = Test(
                @"
                    using System.Drawing;
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.textBox.Font = new System.Drawing.Font(""Microsoft Sans Serif"", 12F);
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            Font font = null;
            AssertEx.That(() => textBoxInfo.TryGetValue("Font", out font));
            AssertEx.That(() => font.FontFamily.Name == "Microsoft Sans Serif");
            AssertEx.That(() => font.Size == 12f);
            AssertEx.That(() => !font.Bold);
            AssertEx.That(() => !font.Italic);
            AssertEx.That(() => !font.Underline);
        }

        [TestMethod]
        public void Complex_Font_properties_are_processed()
        {
            var components = Test(
                @"
                    using System.Drawing;
                    using System.Runtime.CompilerServices;
                    using System.Windows.Forms;

                    public class Class : Form
                    {
                        private TextBox textBox;

                        private void InitializeComponent()
                        {
                            this.textBox = new TextBox();
                            this.textBox.Font = new System.Drawing.Font(""Microsoft Sans Serif"", 12F, FontStyle.Bold | FontStyle.Italic);
                        }
                    }
                ");

            Field textBoxField = components.Type.GetField(Identifier.For("textBox"));

            ComponentInfo textBoxInfo = null;
            AssertEx.That(() => components.TryGetValue(textBoxField, out textBoxInfo));
            Font font = null;
            AssertEx.That(() => textBoxInfo.TryGetValue("Font", out font));
            AssertEx.That(() => font.FontFamily.Name == "Microsoft Sans Serif");
            AssertEx.That(() => font.Size == 12f);
            AssertEx.That(() => font.Bold);
            AssertEx.That(() => font.Italic);
            AssertEx.That(() => !font.Underline);
        }

        #region Private Methods
        private static ComponentDictionary Test(string code)
        {
            var assembly = new CompileHelper()
                .AddAssemblyReference("System.Drawing.dll")
                .AddAssemblyReference("System.Windows.Forms.dll")
                .AddSource(code)
                .Generate(true);
            var type = TypeNodeHelper.GetTypeNode(assembly, "Class");
            return new ComponentDictionary(type);
        }

        private static ComponentDictionary Test(string code, Action<ResourceWriter> writeAction)
        {
            var assembly = new CompileHelper()
                .AddAssemblyReference("System.Drawing.dll")
                .AddAssemblyReference("System.Windows.Forms.dll")
                .AddSource(code)
                .AddEmbeddedResource("Class", writeAction)
                .Generate(true);
            var type = TypeNodeHelper.GetTypeNode(assembly, "Class");
            return new ComponentDictionary(type);
        }
        #endregion Private Methods
    }
}